﻿/*
 * Created by SharpDevelop.
 * User: Emanuel
 * Date: 24-05-2013
 * Time: 14:16
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;

namespace Client
{
	/// <summary>
	/// Classe client.
	/// Responsavel pela ligação ao servidor;
	/// Guardar dados referentes ao utilizador;
	/// Tratar de todos as operações com o servidor.
	/// </summary>
	public class Cliente
	{
		public bool emUso = false;
		public bool sessaoFechada { get; set; }
		public string username { get; set; }
		public string password { get; set; }
		public int id { get; set; }
		public IList<Leiloes.Leilao> listaLicitarAuto = new List<Leiloes.Leilao>();

		private ArrayList pacote = new ArrayList();
		private IEnumerator lista;
		private int qtBytes;
		private byte[] buffer = new byte[1024];
		private Socket socketCliente = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
		private IPEndPoint ie = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6000);
        

		/// <summary>
		/// Construtor
		/// Conexão com o servidor
		/// 
		public Cliente()
		{
			try
			{
				socketCliente.Connect(ie);
				sessaoFechada = false;

			}
			catch (Exception)
			{
				Console.WriteLine("Falha comunicação . . . ");
				socketCliente.Close();
				sessaoFechada = true;
				Console.ReadKey();
			}
		}

		/// <summary>
		/// Metodo para ver o saldo do utilizador
		/// </summary>
		/// <returns>Saldo actual</returns>
		public double verSaldo()
		{
            emUso = true;
			pacote.Clear();
			pacote.Add("VSALDO.REQ");
			pacote.Add(id.ToString());
			socketCliente.Send(criaPacote(pacote)); //Request
			
			qtBytes = socketCliente.Receive(buffer); //Confirm
			lista = abrePacote(buffer);
			lista.MoveNext();
			emUso = false;

			
            return double.Parse(lista.Current.ToString());
		}

		/// <summary>
		/// Método para o login
		/// </summary>
		/// <returns>Verdadeiro caso a autenticação bem sucedida</returns>
		public bool login()
		{
            emUso = true;
			pacote.Clear();
			pacote.Add("AUTH.REQ");
			pacote.Add(username);
			pacote.Add(password);

			socketCliente.Send(criaPacote(pacote)); //Request
			
			qtBytes = socketCliente.Receive(buffer); //Confirm
			lista = abrePacote(buffer);
			lista.MoveNext();
			string dataStr = lista.Current.ToString();
			lista.MoveNext();
			string ret = lista.Current.ToString();
			if (ret == "false")
			{
				Console.WriteLine("\nAutenticação inválida");
				socketCliente.Send(Encoding.ASCII.GetBytes("1")); //Request
				Console.ReadLine();
				sair();
				return false;
			}
			else
			{
				lista.MoveNext();
				id = int.Parse(lista.Current.ToString());
				Timer t = new Timer(ComputeBoundOp, 0, 1000, 5000);
                //Timer t = new Timer(ComputeBoundOp);
                
			}
			emUso = false;

			return true;
		}

		/// <summary>
		/// Método para depositar dinheiro na conta do utilizador
		/// </summary>
		/// <param name="valor"></param>
		/// <returns>Verdadeiro caso o deposito foi bem sucedido</returns>
		public bool depositar(double valor)
		{
            emUso = true;
            pacote.Clear();
			pacote.Add("ADEPOS.REQ");
			pacote.Add(id.ToString());
			pacote.Add(valor);
			socketCliente.Send(criaPacote(pacote)); //Request
			
			qtBytes = socketCliente.Receive(buffer); //Confirm
			lista = abrePacote(buffer);
			lista.MoveNext();
			Console.WriteLine("Saldo atual: " + lista.Current.ToString());
			emUso = false;

			return true;
		}
		/// <summary>
		/// Obter a lista de leilões em que o cliente já licitou
		/// </summary>
		/// <returns></returns>
		public IList<Leiloes.Leilao> verAtivos()
		{
            emUso = true;
			IList<Leiloes.Leilao> leilaoAtivos = new List<Leiloes.Leilao>();
			
			pacote.Clear();
			pacote.Add("LLATIV.REQ");
			pacote.Add(id.ToString());

			socketCliente.Send(criaPacote(pacote));//Request
			
			qtBytes = socketCliente.Receive(buffer); //Confirm
			lista = abrePacote(buffer);
			while (lista.MoveNext())
			{
				leilaoAtivos.Add((Leiloes.Leilao)lista.Current);
			}
			emUso = false;

			return leilaoAtivos;
		}
		
		/// <summary>
		/// Obter a lista de leilões em que o cliente ainda não licitou
		/// </summary>
		/// <returns></returns>
		public IList<Leiloes.Leilao> verDisponiveis() {
			IList<Leiloes.Leilao> leilDisponiveis = new List<Leiloes.Leilao>();
			
			pacote.Clear();
			pacote.Add("LLDISP.REQ");
			pacote.Add(id.ToString());

			
			socketCliente.Send(criaPacote(pacote));//Request
			emUso = true;
			qtBytes = socketCliente.Receive(buffer); //Confirm
			lista = abrePacote(buffer);
			while (lista.MoveNext())
			{
				leilDisponiveis.Add((Leiloes.Leilao)lista.Current);
			}
			//int i = int.Parse(lista.);
			//while (i>0)
			//{
			//    i--;
			//    leilDisponiveis.Add((Leiloes.Leilao)lista.Current);
			//    lista.MoveNext();
			//}
			emUso = false;
			return leilDisponiveis;
		}
		
		/// <summary>
		/// Método responsável por fazer o kick-off do criar leilao
		/// </summary>
		/// <param name="nLeilao"></param>
		/// <returns></returns>
		public bool criarLeilao(Leiloes.Leilao nLeilao) {
			pacote.Clear();
			pacote.Add("ALEILAO.REQ");
			pacote.Add(nLeilao);
			socketCliente.Send(criaPacote(pacote)); //Request
			emUso = true;
			qtBytes = socketCliente.Receive(buffer); //Confirm
			
			lista = abrePacote(buffer);
			lista.MoveNext(); //Para aceder ao primeiro elemento da lista
			int id = (int)lista.Current;
			emUso = false;
			
			if (id==-1)
				return false;
			return true;
		}
		
		/// <summary>
		/// Método responsável por licitar um leilão préviamente selecionado
		/// </summary>
		/// <param name="idLeilao"></param>
		/// <param name="valor"></param>
		/// <returns>true= licitação bem sucedida; false=mal sucedida</returns>
		public bool licitar(int idLeilao, double valor) {
			pacote.Clear();
			pacote.Add("LICITAR.REQ");
			pacote.Add(this.id); // id do comprador/utilizador
			pacote.Add(valor); //valor a licitar
			pacote.Add(idLeilao); //id do leilão
			socketCliente.Send(criaPacote(pacote)); //Request ----->
			emUso = true;
			qtBytes = socketCliente.Receive(buffer); //Confirm <-----
			
			lista = abrePacote(buffer);
			lista.MoveNext(); //Para aceder ao primeiro elemento da coleção
			bool sucesso = (bool)lista.Current;
			emUso = false;
			return sucesso;
		}
		
		/// <summary>
		/// Método responsável para devolver o objeto leilão
		/// </summary>
		/// <param name="idLeilao"></param>
		/// <returns></returns>
		public Leiloes.Leilao detalhe(int idLeilao) {
			pacote.Clear();
			pacote.Add("DLEILAO.REQ");
			pacote.Add(idLeilao); // id do leilao
			socketCliente.Send(criaPacote(pacote)); //Request ----->
			emUso = true;
			qtBytes = socketCliente.Receive(buffer); //Confirm <-----
			
			
			lista = abrePacote(buffer);
			lista.MoveNext();//Para aceder ao primeiro elemento da coleção
			emUso = false;
			return (Leiloes.Leilao)lista.Current;
		}
		
		/// <summary>
		/// Método para fechar conexão com o servidor
		/// </summary>
		public void sair()
		{
            pacote.Clear();
            pacote.Add("exit");
            socketCliente.Send(criaPacote(pacote)); 
            socketCliente.Disconnect(true);
			socketCliente.Close();
			sessaoFechada = true;
			emUso = false;
		}

		/// <summary>
		/// Arraylist->Stream->Serialize->byte[]
		/// </summary>
		/// <param name="pacote"></param>
		/// <returns>Byte[] para ser enviado pelo socket</returns>
		private byte[] criaPacote(ArrayList pacote)
		{
			MemoryStream fs = new MemoryStream();

			BinaryFormatter formatter = new BinaryFormatter();

			formatter.Serialize(fs, pacote);

			byte[] bufer = fs.ToArray();

			return bufer;
		}

		/// <summary>
		/// byte[]->stream->deserialize->arraylist->enumerator
		/// </summary>
		/// <param name="bufer"></param>
		/// <returns>Devolve o enumerator do pacote recebido</returns>
		private IEnumerator abrePacote(Byte[] bufer)
		{
			try
			{
				MemoryStream ms = new MemoryStream(bufer);
				ms.Position = 0;

				BinaryFormatter formatter = new BinaryFormatter();

				ArrayList pacote = (ArrayList)formatter.Deserialize(ms);
				ms.Flush();
				return pacote.GetEnumerator();
			}
			catch (Exception e)
			{
				Console.WriteLine(e.ToString());
				return pacote.GetEnumerator();
			}
		}
		
		/// <summary>
		/// Inicia as licitações automáticas
		/// </summary>
		/// <param name="noticLicitarAuto"></param>
		/// <returns></returns>
		private bool noticLicitarAuto(Leiloes.Leilao noticLicitarAuto, Leiloes.Leilao leilaoNoti) {
            emUso = true;
            Leiloes.Leilao leilaoLicitar = ((Leiloes.Leilao)listaLicitarAuto.Where(i=>i.id==noticLicitarAuto.id).FirstOrDefault<Leiloes.Leilao>());
			if(leilaoLicitar!=null) {
				if (noticLicitarAuto.valorAtual>leilaoLicitar.valorMax) {
					Console.WriteLine("Notificação do sistema: O valor das licitações ultrapassou o valor máximo que o utilizador está disposto a pagar pelo artigo");
					listaLicitarAuto.Remove(leilaoLicitar);
                    emUso = false;
					return false;
				}
				licitar(leilaoLicitar.id,(noticLicitarAuto.valorAtual+leilaoLicitar.valorIncr));
                leilaoNoti.valorAtual = noticLicitarAuto.valorAtual + leilaoLicitar.valorIncr;
                emUso = false; 
                return true;
			}
            emUso = false;
			return false;
		}

        private void noticAuto()
        {
            IList<Leiloes.Leilao> listaAtivos = verAtivos();
            if (listaAtivos != null)
            {
                foreach(var element in listaAtivos) {
                    foreach(var element2 in listaLicitarAuto) {
                        if (element.id == element2.id && element.valorAtual != element2.valorAtual)
                        {
                            if (noticLicitarAuto(element,element2))
                            {
                                Console.WriteLine("Notificação do sistema: licitação automática: {0}" + element.nome);
                            }
                        }
                    }
                }
            }
            emUso = false;
        }
		
		/// <summary>
		/// Timer de notificações
		/// Pergunta ao servidor se tem notificações pendentes
		/// </summary>
		/// <param name="state"></param>
		private void ComputeBoundOp(Object state)
        {
            Console.WriteLine("Entrei no timer:" + emUso);
            
            try
            {
                if (emUso == false)
                {
                    emUso = true;
                    pacote.Clear();
                    pacote.Add("NOTIC.REQ");
                    pacote.Add(id);
                    socketCliente.Send(criaPacote(pacote)); //Request
                    qtBytes = socketCliente.Receive(buffer); //Confirm
                    lista = abrePacote(buffer);
                    while (lista.MoveNext())
                    {
                        
                        /*if (noticLicitarAuto(((Leiloes.Notificar)lista.Current)))
                        {
                            Console.WriteLine("Notificação do sistema: licitação automática" + lista.Current.ToString());
                        }
                        else
                        {*/
                            Console.WriteLine("Notificação do sistema: " + ((Leiloes.Notificar)lista.Current).ToString());
                        
                        //  }
                    }

                    if (listaLicitarAuto.Count > 0)
                    {
                        noticAuto();
                    }
                    emUso = false;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Erro no TIMER " + e.ToString()); 
            }
		}

	}
}
